#include "point.h"
#include "utilities.h"
#include <cmath>
#include <QDebug>

GeometryUtilities::Point::Point()
{
}

GeometryUtilities::Point::Point(qreal x, qreal y)
  : QPointF(x, y)
{
}

GeometryUtilities::Point::Point(const QPoint &qpt)
  : QPointF(qpt)
{
}

GeometryUtilities::Point::Point(const QPointF &qpt)
  : QPointF(qpt)
{
}

qreal GeometryUtilities::Point::magnitude() const
{
  return sqrt(x()*x()+y()*y());
}

GeometryUtilities::Point GeometryUtilities::Point::operator/(qreal scaler) const
{
  return Point(x()/scaler, y()/scaler);
}

GeometryUtilities::Point GeometryUtilities::Point::operator*(qreal scaler) const
{
  return Point(x()*scaler, y()*scaler);
}

GeometryUtilities::Point GeometryUtilities::Point::operator-(const GeometryUtilities::Point& point) const
{
  return Point(x()-point.x(), y()-point.y());
}

GeometryUtilities::Point GeometryUtilities::Point::operator+(const GeometryUtilities::Point& point) const
{
  return Point(x()+point.x(), y()+point.y());
}

const GeometryUtilities::Point& GeometryUtilities::Point::operator+=(const GeometryUtilities::Point& point)
{
  setX(x()+point.x());
  setY(y()+point.y());
  return *this;
}

const GeometryUtilities::Point& GeometryUtilities::Point::operator-=(const GeometryUtilities::Point& point)
{
  setX(x()-point.x());
  setY(y()-point.y());
  return *this;
}

const GeometryUtilities::Point& GeometryUtilities::Point::operator/=(qreal scaler)
{
  if (!Utilities::floatEquality(scaler, 0, 0.001))
  {
    setX(x()/scaler);
    setY(y()/scaler);
  }
  else
  {
    qDebug() << "Divide by 0 occurred in GeometryUtilities::Point";
  }
  return *this;
}

const GeometryUtilities::Point& GeometryUtilities::Point::operator*=(qreal scaler)
{
  setX(x()*scaler);
  setY(y()*scaler);
  return *this;
}

qreal GeometryUtilities::Point::operator*(const GeometryUtilities::Point &point) const
{
  return dot(point);
}

qreal GeometryUtilities::Point::dot(const GeometryUtilities::Point &point) const
{
  return x()*point.x() + y()*point.y();
}

qreal GeometryUtilities::Point::perpdot(const Point &point) const
{
  return x()*point.y()-y()*point.x();
}

bool GeometryUtilities::Point::operator<(const GeometryUtilities::Point & rhs) const
{
  return magnitude() < rhs.magnitude();
}

bool GeometryUtilities::Point::operator ==(const GeometryUtilities::Point& rhs) const
{
  return Utilities::floatEquality(x(), rhs.x()) &&
      Utilities::floatEquality(y(), rhs.y());
}

QString GeometryUtilities::Point::toString(const unsigned int precision) const
{
  return QString("(") + QString::number(x(),'f',precision) + ", " + QString::number(y(),'f',precision) + ")";
}

// For unit circle values, see below.
//       .  90 .
// 180/-180       0
//       . -90  .
qreal GeometryUtilities::Point::angle(const bool clamp0to360) const
{
  qreal ret = (qreal)(atan2(y(), x())*(180.0/M_PI));
  if (clamp0to360)
    Point::clampAngleTo360(ret);
  else
    Point::clampAngleTo180(ret);
  return ret;
}

qreal GeometryUtilities::Point::clampAngleTo360(const qreal value)
{
  qreal ret = value;
  while (ret >= 360.0f) ret -= 360.0f;
  while (ret < 0.0f) ret += 360.0f;
  return ret;
}

qreal GeometryUtilities::Point::clampAngleTo180(const qreal value)
{
  qreal ret = value;
  while (ret >= 180.0f) ret -= 360.0f;
  while (ret < -180.0f) ret += 360.0f;
  return ret;
}

qreal GeometryUtilities::Point::angle(const Point &point1, const Point &point2)
{
  return acos((point1*point2) / (point1.magnitude()*point2.magnitude()));
}

qreal GeometryUtilities::Point::signedangle(const Point &point1, const Point &point2)
{
  return atan2(point1.perpdot(point2), point1.dot(point2));
}

qreal GeometryUtilities::Point::bearing(const Point &point1, const Point &point2)
{
  GeometryUtilities::Point p = point2-point1;
  return p.angle();
}

qreal GeometryUtilities::Point::cross(const GeometryUtilities::Point &point) const
{
  return (x() * point.y()) - (y() * point.x());
}

void GeometryUtilities::Point::normalize()
{
  qreal mag = magnitude();
  if (mag==0)
    return;
  setX(x()/mag);
  setY(y()/mag);
}

GeometryUtilities::Point GeometryUtilities::Point::normalized() const
{
  Point ret(*this);
  ret.normalize();
  return ret;
}



